//! 内存工具
//! 
//! 提供内存使用情况查询功能

/// 内存信息结构体
#[derive(Debug, Clone)]
pub struct MemoryInfo {
    /// 总内存（字节）
    pub total: u64,
    /// 已使用内存（字节）
    pub used: u64,
    /// 可用内存（字节）
    pub free: u64,
    /// 内存使用率（百分比）
    pub usage_percent: f64,
}

/// 内存错误类型
#[derive(Debug, thiserror::Error)]
pub enum MemoryError {
    #[error("Failed to read memory info: {0}")]
    ReadFailed(String),
    #[error("Failed to parse memory info: {0}")]
    ParseFailed(String),
    #[error("Unsupported platform")]
    UnsupportedPlatform,
}

pub type MemoryResult<T> = Result<T, MemoryError>;

/// 获取内存使用情况
/// 
/// 返回 (used_memory, total_memory) 以字节为单位
/// 
/// # Examples
/// ```
/// use librarys::system::get_memory_usage;
/// 
/// let (used, total) = get_memory_usage().unwrap();
/// assert!(used <= total);
/// assert!(total > 0);
/// ```
pub fn get_memory_usage() -> Result<(u64, u64), Box<dyn std::error::Error>> {
    let memory_info = get_memory_info()?;
    Ok((memory_info.used, memory_info.total))
}

/// 获取详细的内存信息
/// 
/// # Examples
/// ```
/// use librarys::system::get_memory_info;
/// 
/// let info = get_memory_info().unwrap();
/// assert!(info.usage_percent >= 0.0 && info.usage_percent <= 100.0);
/// assert_eq!(info.used + info.free, info.total);
/// ```
pub fn get_memory_info() -> MemoryResult<MemoryInfo> {
    #[cfg(target_os = "windows")]
    {
        get_memory_info_windows()
    }
    #[cfg(target_os = "linux")]
    {
        get_memory_info_linux()
    }
    #[cfg(target_os = "macos")]
    {
        get_memory_info_macos()
    }
    #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
    {
        Err(MemoryError::UnsupportedPlatform)
    }
}

#[cfg(target_os = "windows")]
fn get_memory_info_windows() -> MemoryResult<MemoryInfo> {
    use std::mem;
    
    #[repr(C)]
    struct MEMORYSTATUSEX {
        dw_length: u32,
        dw_memory_load: u32,
        ull_total_phys: u64,
        ull_avail_phys: u64,
        ull_total_page_file: u64,
        ull_avail_page_file: u64,
        ull_total_virtual: u64,
        ull_avail_virtual: u64,
        ull_avail_extended_virtual: u64,
    }
    
    extern "system" {
        fn GlobalMemoryStatusEx(lpBuffer: *mut MEMORYSTATUSEX) -> i32;
    }
    
    let mut mem_status = MEMORYSTATUSEX {
        dw_length: mem::size_of::<MEMORYSTATUSEX>() as u32,
        dw_memory_load: 0,
        ull_total_phys: 0,
        ull_avail_phys: 0,
        ull_total_page_file: 0,
        ull_avail_page_file: 0,
        ull_total_virtual: 0,
        ull_avail_virtual: 0,
        ull_avail_extended_virtual: 0,
    };
    
    let result = unsafe { GlobalMemoryStatusEx(&mut mem_status) };
    
    if result == 0 {
        return Err(MemoryError::ReadFailed("GlobalMemoryStatusEx failed".to_string()));
    }
    
    let total = mem_status.ull_total_phys;
    let free = mem_status.ull_avail_phys;
    let used = total - free;
    let usage_percent = (used as f64 / total as f64) * 100.0;
    
    Ok(MemoryInfo {
        total,
        used,
        free,
        usage_percent,
    })
}

#[cfg(target_os = "linux")]
fn get_memory_info_linux() -> MemoryResult<MemoryInfo> {
    use std::fs;
    
    let content = fs::read_to_string("/proc/meminfo")
        .map_err(|e| MemoryError::ReadFailed(format!("Failed to read /proc/meminfo: {}", e)))?;
    
    let mut total = 0;
    let mut available = 0;
    
    for line in content.lines() {
        if line.starts_with("MemTotal:") {
            total = parse_memory_line(line)?
                .ok_or_else(|| MemoryError::ParseFailed("Failed to parse MemTotal".to_string()))?;
        } else if line.starts_with("MemAvailable:") {
            available = parse_memory_line(line)?
                .ok_or_else(|| MemoryError::ParseFailed("Failed to parse MemAvailable".to_string()))?;
        }
    }
    
    if total == 0 {
        return Err(MemoryError::ParseFailed("Total memory not found".to_string()));
    }
    
    // 如果没有MemAvailable，使用MemFree + Buffers + Cached
    if available == 0 {
        let mut free = 0;
        let mut buffers = 0;
        let mut cached = 0;
        
        for line in content.lines() {
            if line.starts_with("MemFree:") {
                free = parse_memory_line(line)?.unwrap_or(0);
            } else if line.starts_with("Buffers:") {
                buffers = parse_memory_line(line)?.unwrap_or(0);
            } else if line.starts_with("Cached:") {
                cached = parse_memory_line(line)?.unwrap_or(0);
            }
        }
        available = free + buffers + cached;
    }
    
    let used = total - available;
    let usage_percent = (used as f64 / total as f64) * 100.0;
    
    Ok(MemoryInfo {
        total: total * 1024, // 转KB为字节
        used: used * 1024,
        free: available * 1024,
        usage_percent,
    })
}

#[cfg(target_os = "linux")]
fn parse_memory_line(line: &str) -> MemoryResult<Option<u64>> {
    let parts: Vec<&str> = line.split_whitespace().collect();
    if parts.len() >= 2 {
        parts[1].parse::<u64>()
            .map(Some)
            .map_err(|e| MemoryError::ParseFailed(format!("Failed to parse number: {}", e)))
    } else {
        Ok(None)
    }
}

#[cfg(target_os = "macos")]
fn get_memory_info_macos() -> MemoryResult<MemoryInfo> {
    use std::process::Command;
    
    // 使用vm_stat命令获取内存信息
    let output = Command::new("vm_stat")
        .output()
        .map_err(|e| MemoryError::ReadFailed(format!("Failed to run vm_stat: {}", e)))?;
    
    if !output.status.success() {
        return Err(MemoryError::ReadFailed("vm_stat command failed".to_string()));
    }
    
    let content = String::from_utf8_lossy(&output.stdout);
    let mut page_size = 4096u64; // 默认页大小
    let mut free_pages = 0u64;
    let mut active_pages = 0u64;
    let mut inactive_pages = 0u64;
    let mut wired_pages = 0u64;
    
    for line in content.lines() {
        if line.contains("page size of") {
            if let Some(size_str) = line.split_whitespace().nth(7) {
                page_size = size_str.parse().unwrap_or(4096);
            }
        } else if line.starts_with("Pages free:") {
            free_pages = parse_pages_line(line).unwrap_or(0);
        } else if line.starts_with("Pages active:") {
            active_pages = parse_pages_line(line).unwrap_or(0);
        } else if line.starts_with("Pages inactive:") {
            inactive_pages = parse_pages_line(line).unwrap_or(0);
        } else if line.starts_with("Pages wired down:") {
            wired_pages = parse_pages_line(line).unwrap_or(0);
        }
    }
    
    let total_pages = free_pages + active_pages + inactive_pages + wired_pages;
    let total = total_pages * page_size;
    let free = free_pages * page_size;
    let used = total - free;
    let usage_percent = if total > 0 { (used as f64 / total as f64) * 100.0 } else { 0.0 };
    
    Ok(MemoryInfo {
        total,
        used,
        free,
        usage_percent,
    })
}

#[cfg(target_os = "macos")]
fn parse_pages_line(line: &str) -> Option<u64> {
    line.split_whitespace()
        .nth(2)?
        .trim_end_matches('.')
        .parse()
        .ok()
}

/// 格式化内存大小（转换为可读的单位）
/// 
/// # Examples
/// ```
/// use librarys::system::format_memory_size;
/// 
/// assert_eq!(format_memory_size(1024), "1.00 KB");
/// assert_eq!(format_memory_size(1048576), "1.00 MB");
/// assert_eq!(format_memory_size(1073741824), "1.00 GB");
/// ```
pub fn format_memory_size(bytes: u64) -> String {
    const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
    let mut size = bytes as f64;
    let mut unit_index = 0;
    
    while size >= 1024.0 && unit_index < UNITS.len() - 1 {
        size /= 1024.0;
        unit_index += 1;
    }
    
    format!("{:.2} {}", size, UNITS[unit_index])
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_memory_usage() {
        let result = get_memory_usage();
        assert!(result.is_ok());
        
        let (used, total) = result.unwrap();
        assert!(used <= total);
        assert!(total > 0);
    }

    #[test]
    fn test_get_memory_info() {
        let result = get_memory_info();
        assert!(result.is_ok());
        
        let info = result.unwrap();
        assert!(info.usage_percent >= 0.0 && info.usage_percent <= 100.0);
        assert!(info.total > 0);
        assert!(info.used <= info.total);
    }

    #[test]
    fn test_format_memory_size() {
        assert_eq!(format_memory_size(0), "0.00 B");
        assert_eq!(format_memory_size(1024), "1.00 KB");
        assert_eq!(format_memory_size(1048576), "1.00 MB");
        assert_eq!(format_memory_size(1073741824), "1.00 GB");
        assert_eq!(format_memory_size(512), "512.00 B");
        assert_eq!(format_memory_size(1536), "1.50 KB");
    }

    #[test]
    fn test_memory_info_consistency() {
        if let Ok(info) = get_memory_info() {
            // 基本一致性检查。在实际系统中，由于内存管理的复杂性，
            // used + free 可能不等于 total，所以这里只检查基本的合理性
            assert!(info.used <= info.total);
            assert!(info.free <= info.total);
            assert!(info.usage_percent <= 100.0);
        }
    }
}